package net.sansi.v3correctserver.util;

import lombok.extern.slf4j.Slf4j;
import net.sansi.v3correctserver.entity.color.PhotoColor;
import net.sansi.v3correctserver.entity.image.RecImage;
import net.sansi.v3correctserver.exception.ServiceException;

import java.awt.*;

/**
 * Created by Fang
 * create time : 2024/10/31
 * IDE : IntelliJ IDEA
 * <p>
 * RGB 工具类
 */
@Slf4j
public class RGBUtil {

    /**
     * 获取 NTSC 亮度
     *
     * @param r red
     * @param g green
     * @param b blue
     * @return NTSC 亮度
     */
    public static double getNTSCBright(double r, double g, double b) {
        return r * 0.299 + g * 0.587 + b * 0.114;
    }

    /**
     * 获取图片亮度
     *
     * @param target     目标图片
     * @param blackPhoto 背景图片
     * @param x          x 坐标
     * @param y          y 坐标
     * @return 亮度
     */
    public static double getBrightness(RecImage target, RecImage blackPhoto, int x, int y) {
        double bright = target.getBrightness(x, y);
        if (blackPhoto != null) {
            bright -= blackPhoto.getBrightness(x, y);
        }
        return bright;
    }

    /**
     * 颜色名称
     *
     * @param idx 颜色索引
     * @return 颜色名称
     */
    public static String colorName(int idx) {
        if (idx == 0) return "red";
        if (idx == 1) return "green";
        if (idx == 2) return "blue";
        if (idx == 3) return "white";
        throw new ServiceException("[colorName] 颜色索引不能大于3 [idx] {}", idx);
    }

    // RGB 到 CIE 的转换的 GAMMA 值
    private static double GAMMA = 2.2;

    /**
     * RGB 转 CIE xyY
     *
     * @param r 红色
     * @param g 绿色
     * @param b 蓝色
     * @return CIE xyY
     */
    public static double[] rgbToCIE(double r, double g, double b) {
        double sum = r + g + b;
        if (sum == 0) return new double[]{0, 0, 0};
        double[] xyz = rgbToXyz(r, g, b);
        double x = xyz[0], y = xyz[1], z = xyz[2];
        sum = x + y + z;
        if (sum == 0) return new double[]{0, 0, 0};
        return new double[]{x / sum, y / sum, z / sum};
    }

    /**
     * rgb 转 xyz
     *
     * @param r 红色
     * @param g 绿色
     * @param b 蓝色
     * @return xyz
     */
    public static double[] rgbToXyz(double r, double g, double b) {
        double sum = r + g + b;
        if (sum == 0) return new double[]{0, 0, 0};
        double rLinear = linearize(r / sum);
        double gLinear = linearize(g / sum);
        double bLinear = linearize(b / sum);
        double x = rLinear * 0.4124564 + gLinear * 0.3575761 + bLinear * 0.1804375;
        double y = rLinear * 0.2126729 + gLinear * 0.7151522 + bLinear * 0.0721750;
        double z = rLinear * 0.0193339 + gLinear * 0.1191920 + bLinear * 0.9503041;
        return new double[]{x, y, z};
    }

    /**
     * CIE 转换为 RGB
     *
     * @param x
     * @param y
     * @param bright
     * @return
     */
    public static double[] cieToRGB(double x, double y, double bright) {
        if (bright == 0) return new double[]{0, 0, 0};
        double z = (1 - x - y) / y;
        x = x / y;
        y = 1;
        log.info("[xyz] {}, {}, {}", x, y, z);
        double r = x * 3.2406 + y * -1.5372 + z * -0.4986;
        double g = x * -0.9689 + y * 1.8758 + z * 0.0415;
        double b = x * 0.0557 + y * -0.204 + z * 1.057;
        r = gammaCorrect(r);
        g = gammaCorrect(g);
        b = gammaCorrect(b);
        double sum = r + g + b;
        log.info("[rgb] {}, {}, {}", r, g, b);
        double scale = bright / sum;
        return new double[]{r * scale, g * scale, b * scale};
    }

    /**
     * 线性化函数
     *
     * @param x
     * @return
     */
    private static double linearize(double x) {
        if (x <= 0.04045) {
            return x / 12.92;
        } else {
            return Math.pow((x + 0.055) / 1.055, GAMMA);
        }
    }

    /**
     * 伽马校正函数
     *
     * @param x
     * @return
     */
    private static double gammaCorrect(double x) {
        if (x <= 0.0031308) {
            return 12.92 * x;
        } else {
            return 1.055 * Math.pow(x, 1.0 / GAMMA - 0.055);
        }
    }

    /**
     * 亮度转换为颜色
     *
     * @param colorIdx   颜色索引 0-3 0-红色 1-绿色 2-蓝色 3-白色
     * @param brightness 亮度 0-255
     * @return 颜色
     */
    public static Color brightToColor(int colorIdx, double brightness) {
        return brightToPhotoColor(colorIdx, brightness).toColor();
    }

    /**
     * 亮度转换为颜色
     *
     * @param colorIdx   颜色索引 0-3 0-红色 1-绿色 2-蓝色 3-白色
     * @param brightness 亮度
     * @return 颜色
     */
    public static PhotoColor brightToPhotoColor(int colorIdx, double brightness) {
        return new PhotoColor(brightToColorArr(colorIdx, brightness));
    }

    /**
     * 亮度转换为颜色
     *
     * @param colorIdx   0-3 0-红色 1-绿色 2-蓝色 3-白色
     * @param brightness 亮度
     * @return 颜色
     */
    public static double[] brightToColorArr(int colorIdx, double brightness) {
        if (colorIdx == 0) return new double[]{brightness, 0, 0};
        if (colorIdx == 1) return new double[]{0, brightness, 0};
        if (colorIdx == 2) return new double[]{0, 0, brightness};
        if (colorIdx == 3) return new double[]{brightness, brightness, brightness};
        throw new ServiceException("[brightToPhotoColor] 颜色索引不能大于3 [idx] {}", colorIdx);
    }

    /**
     * 限制某个数在某个范围内
     * 四舍五入返回整形结果
     *
     * @param value 值
     * @param min   允许最小值
     * @param max   允许最大值
     * @return 结果
     */
    public static int rangeInt(double value, int min, int max) {
        return (int) Math.round(Math.max(min, Math.min(value, max)));
    }
}
